﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using RPGProject.Data_Structures;
using System.IO;
using Microsoft.Xna.Framework.Input;
namespace RPGProject.LoopSystem
{
    /// <summary>
    /// A class that manages all the loops in the game.
    /// </summary>
    class LoopManager
    {
        /// <summary>
        /// A list of loops.
        /// </summary>
        private PriorityQueue<int, Loop> funcs;
        // Variables for a loop that will be cycled to alternatingly.
        private Loop cycleTo;
        private bool cycled;
        /// <summary>
        /// A list of loops to add, kept seperate as to let the queue flush them at the proper time.
        /// </summary>
        private Vector<Tuple<Loop, FuncType>> funcsToAdd;
        /// <summary>
        /// Current keyboard state, updated by the game class.
        /// </summary>
        private KeyboardState keyState;
        /// <summary>
        /// Gets the current delay of the loop.
        /// </summary>
        public int CurrentDelay
        {
            get 
            {
                if (!this.cycled && this.cycleTo != null)
                    return this.cycleTo.Delay;
                if (this.funcs.Size == 0)
                    return 0;
                return this.funcs.Peek().Delay; 
            }
        }
        /// <summary>
        /// Construts a basic LoopManager.
        /// </summary>
        public LoopManager()
        {
            this.funcs = new PriorityQueue<int, Loop>();
            this.funcsToAdd = new Vector<Tuple<Loop, FuncType>>();
            this.cycled = true;
            this.cycleTo = null;
        }
        /// <summary>
        /// Sets the loop to cycle to.
        /// </summary>
        public Loop CycleTo
        {
            get { return this.cycleTo; }
            set { this.cycleTo = value; }
        }
        /// <summary>
        /// Loops once through the list of functions.  Returns if its finished.
        /// </summary>
        /// <returns></returns>
        public bool LoopOnce()
        {
            if (this.cycled || this.cycleTo == null)
            {
                this.cycled = false;
                Loop current = funcs.Peek();
                switch (current.RunOnce())
                {
                    case FuncType.ADD_TO_END:
                        if (this.funcs.Size > 0)
                            this.funcs.Dequeue();
                        this.AddFunc(FuncType.ADD_TO_END, current);
                        break;
                    case FuncType.ADD_TO_FRONT:
                        this.AddFunc(FuncType.ADD_TO_FRONT, current);
                        break;
                    case FuncType.REPEAT:
                        break;
                    case FuncType.REMOVE_THIS:
                        if (this.funcs.Size > 0 && this.funcs.Peek() == current)
                            this.funcs.Dequeue();
                        break;
                }
            }
            else
            {
                this.cycled = true;
                this.cycleTo.RunOnce();
                this.Flush();
                return true;
            }
            this.Flush();
            return this.funcs.Size > 0;
        }
        /// <summary>
        /// Flushes all the currently waiting loops.  
        /// </summary>
        public void Flush()
        {
            foreach (Tuple<Loop, FuncType> r in this.funcsToAdd)
            {
                if (r.Item2 == FuncType.PRIORITY_TO_FRONT)
                    this.HAddFunc(r.Item2, r.Item1);
            }
            foreach (Tuple<Loop, FuncType> r in this.funcsToAdd)
            {
                if (r.Item2 == FuncType.ADD_TO_FRONT)
                    this.HAddFunc(r.Item2, r.Item1);
            }
            foreach (Tuple<Loop, FuncType> r in this.funcsToAdd)
            {
                if (r.Item2 == FuncType.ADD_TO_END)
                    this.HAddFunc(r.Item2, r.Item1);
            }
            this.funcsToAdd.Clear();
        }
        /// <summary>
        /// Adds a function to the waiting buffer.
        /// </summary>
        /// <param name="type"></param>
        /// <param name="func"></param>
        public void AddFunc(FuncType type, Loop func)
        {
            this.funcsToAdd.Add(Tuple.Create<Loop, FuncType>(func, type));
        }
        /// <summary>
        /// Actually adds the function the buffer.
        /// </summary>
        /// <param name="type"></param>
        /// <param name="func"></param>
        private void HAddFunc(FuncType type, Loop func)
        {
            switch (type)
            {
                case FuncType.ADD_TO_END:
                case FuncType.ADD_TO_FRONT:
                    this.funcs.Enqueue(1, func);
                    break;
                case FuncType.PRIORITY_TO_FRONT:
                    this.funcs.Enqueue(0, func);
                    break;
            }
        }
        /// <summary>
        /// Clears all of the functions and the buffer.
        /// </summary>
        public void ClearAllFuncs()
        {
            this.funcs.Clear();
            this.funcsToAdd.Clear();
        }
        /// <summary>
        /// Gets and state the keyboard set.
        /// </summary>
        public KeyboardState KeyState 
        {
            get { return this.keyState; }
            set { this.keyState = value; }
        }
    }
    enum FuncType
    {
        PRIORITY_TO_FRONT,
        ADD_TO_FRONT,
        ADD_TO_END,
        REPEAT,
        REMOVE_THIS
    }
}
